/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

#include "stdafx.h"
#include "DConfigGlobals.h"
#include "SystemModule.h"
#include "Interpreter.h"
//#include "XTime.h"
#include <afxdisp.h> // COleDateTime

//#include <mmsystem.h> // PlaySound

#ifndef UNDER_CE
#	include "time.h"
#endif

///////////////////////////////////
#if defined(_DEBUG) && (defined(__WINCE__) || defined(_WINNT__))
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////

#define XERR(x) pInterp->RuntimeError(_T(x))
#define ERR(x) {XERR(x);return AllocNil();}
#define TERR(x) {pInterp->RuntimeError(x);return AllocNil();}

static LPCTSTR lpzWrongNumArgs = _T("Wrong number of arguments to function");
static LPCTSTR lpzExpectString = _T("Expected string argument to function.");
static LPCTSTR lpzExpectInt = _T("Expected integer argument to function");
static LPCTSTR lpzExpectArray = _T("Expected array argument to function");
static LPCTSTR lpzExpectNum = _T("Expected number argument to function");

#define REQUIRE_ARGS(x)		if (argCount != x) TERR(lpzWrongNumArgs)
#define REQUIRE_STRING(x)	if (args[x].Type() != stString) TERR(lpzExpectString)
#define REQUIRE_INTEGER(x)	if ((args[x].Type() != stInteger)) TERR(lpzExpectInt)
#define REQUIRE_ARRAY(x)	if ((args[x].Type() != stArray)) TERR(lpzExpectArray)
#define REQUIRE_NUMBER(x)	if ((args[x].Type() != stInteger) && (args[x].Type() != stFloat)) TERR(lpzExpectNum)

/*#define XERR(x) pInterp->RuntimeError(_T(x))
#define ERR(x) {XERR(x);return AllocNil();}

#define REQUIRE_ARGS(x) if (argCount!=x) ERR("Wrong number of arguments to function.")
#define REQUIRE_STRING(x) if (args[x].Type() != stString) ERR("Expected string in argument(s) to function.")
#define REQUIRE_FLOAT(x) if ((args[x].Type() != stFloat)||(args[x].Type() != stInteger)) ERR("Expected number in argument(s) to function.")
#define REQUIRE_INTEGER(x) if ((args[x].Type() != stInteger)) ERR("Expected integer in arguments(s) to function.")
#define REQUIRE_ARRAY(x) if ((args[x].Type() != stArray)) ERR("Expected array in arguments(s) to function.")
*/

static VALUE vRegKeyClassBase; // Used by system_openregkey() and InitSystemModule()
#ifdef COMPORT_SUPPORT
	static VALUE vComPortClassBase;
#endif
#ifdef DLL_SUPPORT
	static VALUE vDLLBaseClass;
#endif

/*#include <winnls.h>
CORE_FUNC(system_readLocaleID)
{
	return (int)GetUserDefaultLCID();
}

CORE_FUNC(system_writeLocaleID)
{
	ASSERT(argCount == 2); // Property write function
#ifdef UNDER_CE
	SetSystemDefaultLCID((LCID)args[1].Integer());
#else
	SetThreadLocale((LCID)args[1].Integer());
#endif
	return AllocNil();
}*/

CORE_FUNC(system_sleep)
{
	REQUIRE_ARGS(1);
	Sleep(args[0].Integer());
	return args[0];
}

CORE_FUNC(system_seconds)
{
	// Seconds since midnight (00:00:00), January 1, 1970
	// coordinated universal time
	REQUIRE_ARGS(0);
#pragma message("********** Possible CTime::GetCurrentTime() bug in CE *************")
	return (int)CTime::GetCurrentTime().GetTime();
//#ifdef UNDER_CE
//	SYSTEMTIME st;
//	GetLocalTime(&st);
//	ERR("Ticks not supported under CE at this time");
//	return (int)0;//wce_mktime(&st);
//#else
//	return (int)time(NULL);
//#endif
}

CORE_FUNC(system_tickcount)
{
	// Since we don't want to return negative numbers,
	// roll to zero every 0x7FFFFFFF milliseconds.
	// Once unsigned integers are supported, we can
	// stop doing that.
	return (int)(GetTickCount() & 0x7FFFFFFF);
}

CORE_FUNC(system_status)
{
	// Return true if the process is still active, else false

	REQUIRE_ARGS(1);
	REQUIRE_INTEGER(0);

	HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)args[0].Integer());
	if (h == NULL) {
		ERR("Unable to get process information");
	}

	DWORD exitCode;
	if (!GetExitCodeProcess(h, &exitCode)) {
		CloseHandle(h);
		ERR("Unable to get process information");
	}

	CloseHandle(h);
	return (bool)(exitCode == STILL_ACTIVE);
}

#ifdef __WINCE__
static bool ParseAppCmdline(TString wholeLine, TString& appName, TString& cmdLine)
{
	// CreateProcess in CE can't take both in one line of text
	// We must split them here
	appName.Empty();
	cmdLine.Empty();

	wholeLine.TrimLeft();
	wholeLine.TrimRight();
	if (wholeLine.GetLength() == 0) {
		return false;
	}

	LPCTSTR x = wholeLine;
	bool bQuote = *x == '\"';
	if (bQuote) {
		++x;
		while((*x != '\0') && (*x <= ' ')) ++x; // Eat white space
	}

	while ((*x != '\0') && (bQuote ? (*x != '\"') : (*x != ' '))) {
		appName += *x++;
	}

	if (bQuote && (*x == '\"')) ++x;

	while (*x != '\0') cmdLine += *x++;

	return true;
}
#endif

CORE_FUNC(system_exec)
{
	if ((argCount != 1) && (argCount != 2)) {
		ERR("Invalid number of args: system.exec(cmdLine, <bWait>)");
	}

	STARTUPINFO si;
	memset(&si, 0, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);

	LPTSTR bufAppName = NULL;
	LPTSTR bufCmdLine = NULL;
	TString cmdLine, appName;

#ifdef __WINCE__
	ParseAppCmdline(args[0].String(), appName, cmdLine); // Silly CE

	bufAppName = new TCHAR[appName.GetLength() + 1];
	_tcscpy(bufAppName, (LPCTSTR)appName);

	if (cmdLine.GetLength() > 0) {
		bufCmdLine = new TCHAR[cmdLine.GetLength() + 1];
		_tcscpy(bufCmdLine, (LPCTSTR)cmdLine);
	}
#else
	cmdLine = args[0].String();
	bufCmdLine = new TCHAR[cmdLine.GetLength() + 1];
	_tcscpy(bufCmdLine, (LPCTSTR)cmdLine);
#endif

	PROCESS_INFORMATION pi;
	BOOL b = CreateProcess(
		bufAppName,				// App name
		bufCmdLine,				// Command line (can't put app here in CE)
		NULL,					// Security attributes
		NULL,					// More security attributes
		FALSE,					// Inherit handles
		0,						// Creation flags
		NULL,					// Environment block
		NULL,					// Current directory
		#ifdef UNDER_CE
			NULL,				// Startup info
		#else
			&si,				// Startup info
		#endif
		&pi						// Process info
		);

	if (bufAppName) delete[] bufAppName;
	if (bufCmdLine) delete[] bufCmdLine;

	if (!b) return 0;

	if (args[1].Boolean()) {
		// Wait for process to terminate...
		// Probably should pump messages on occassion
		WaitForSingleObject(pi.hProcess, INFINITE);
	}

	CloseHandle(pi.hProcess);	// Don't hold on to this

	return (int)pi.dwProcessId;
}

CORE_FUNC(system_parsetime)
{
	REQUIRE_ARGS(1);
	VALUE vResult;
	COleDateTime t;
	_TRY {
		if (t.ParseDateTime(args[0].String())) {
			vResult = AllocFrame();
			vResult.Set(_T("year"), (int)t.GetYear());
			vResult.Set(_T("month"), (int)t.GetMonth());
			vResult.Set(_T("day"), (int)t.GetDay());
			vResult.Set(_T("hour"), (int)t.GetHour());
			vResult.Set(_T("minute"), (int)t.GetMinute());
			vResult.Set(_T("second"), (int)t.GetSecond());
			vResult.Set(_T("dayofweek"), (int)t.GetDayOfWeek());
			vResult.Set(_T("dayofyear"), (int)t.GetDayOfYear());
		}
	}
	_CATCH_ALL {
		vResult = AllocNil();
	}
	return vResult;
}

CORE_FUNC(system_datetofloat)
{
	REQUIRE_ARGS(1);
	VALUE vResult;
	COleDateTime t;
	_TRY {
		if (t.ParseDateTime(args[0].String())) {
			vResult = (float)(DATE)t;
		}
	}
	_CATCH_ALL {
		vResult = AllocNil();
	}
	return vResult;
}

CORE_FUNC(system_floattodate)
{
	REQUIRE_ARGS(1);
	DATE df = (DATE)args[0].Float();
	COleDateTime olet(df);
	SYSTEMTIME st;
	if (!olet.GetAsSystemTime(st)) {
		return AllocNil();
	}

	CString d, t;
	LPTSTR buf = d.GetBuffer(128);
	int count = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buf, 128);
	d.ReleaseBuffer(count > 0 ? count - 1 : 0);
	buf = t.GetBuffer(128);
	count = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buf, 128);
	t.ReleaseBuffer(count > 0 ? count - 1 : 0);
	return d + " " + t;
}

static void freeRegKey(void* pVoid)
{
	if (pVoid != NULL) {
		RegCloseKey((HKEY)pVoid);
	}
}

static HKEY _OpenRegKey(TString str, bool bCreateOK)
{
	int pos = str.Find('\\');
	if (pos < 0) return NULL;
	CString mainKey = str.Left(pos);
	CString subKey = str.Mid(pos + 1);
	if (subKey[subKey.GetLength() - 1] == '\\') {
		subKey = subKey.Left(subKey.GetLength() - 1);
	}

	HKEY hMainKey, hOpenedKey;
	mainKey.MakeUpper();
	if (mainKey == _T("HKEY_CLASSES_ROOT")) hMainKey = HKEY_CLASSES_ROOT;
	else if (mainKey == _T("HKEY_CURRENT_USER")) hMainKey = HKEY_CURRENT_USER;
	else if (mainKey == _T("HKEY_LOCAL_MACHINE")) hMainKey = HKEY_LOCAL_MACHINE;
	else if (mainKey == _T("HKEY_USERS")) hMainKey = HKEY_USERS;
	else return NULL;

	DWORD dw;
	if (bCreateOK) {
		#ifdef UNDER_CE
			if (RegCreateKeyEx(hMainKey, (LPCTSTR)subKey, 0, _T(""), 0, 0,
				NULL, &hOpenedKey, &dw) != ERROR_SUCCESS)
		#else
			if (RegCreateKeyEx(hMainKey, (LPCTSTR)subKey, 0, _T(""), REG_OPTION_NON_VOLATILE,
				KEY_READ | KEY_WRITE, NULL, &hOpenedKey, &dw) != ERROR_SUCCESS)
		#endif
			{
				return NULL;
			}
	}
	else {
		#ifdef UNDER_CE
			if (RegOpenKeyEx(hMainKey, (LPCTSTR)subKey, 0, 0, &hOpenedKey) != ERROR_SUCCESS)
		#else
			if (RegOpenKeyEx(hMainKey, (LPCTSTR)subKey, 0, KEY_READ | KEY_WRITE,
				&hOpenedKey) != ERROR_SUCCESS)
		#endif
			{
				return NULL;
			}
	}
	return hOpenedKey;
}

CORE_FUNC(Regkey__Init__)
{
	ASSERT_SELF(SYSTEM_MODULE_NAME);
	if ((argCount != 1) || (args[0].Type() != stString)) {
		pInterp->RuntimeError(_T("system.regkey() reguires one string argument"));
		return AllocNil();
	}

	HKEY hKey = _OpenRegKey(args[0].String(), true);
	if (hKey == NULL) {
		pInterp->RuntimeError(_T("system.regkey() failed to create or open key: ") +
			args[0].String());
		return AllocNil();
	}

	self->Set(_T("#hkey"), AllocVoid(hKey, freeRegKey, _T("regkey")));
	return VALUE();
}

CORE_FUNC(Regkey_SetInt)
{
	ASSERT_SELF(SYSTEM_MODULE_NAME);
	if ((argCount != 2) || (args[0].Type() != stString) || (args[1].Type() != stInteger)) {
		pInterp->RuntimeError(_T("regkey.setint() requires two args <key, intValue>"));
		return AllocNil();
	}
	HKEY hKey = (HKEY)self->Get(_T("#hkey")).Void();
	if (hKey == NULL) {
		pInterp->RuntimeError(_T("regkey not initialized"));
		return AllocNil();
	}
	DWORD value = args[1].Integer();
	if (RegSetValueEx(hKey, args[0].String(), 0, REG_DWORD, (const BYTE*)&value,
		sizeof(DWORD)) != ERROR_SUCCESS)
	{
		pInterp->RuntimeError(_T("regkey.setint() failed"));
		return AllocNil();
	}
	return args[1];
}

CORE_FUNC(Regkey_SetString)
{
	ASSERT_SELF(SYSTEM_MODULE_NAME);
	if ((argCount != 2) || (args[0].Type() != stString)) {
		pInterp->RuntimeError(_T("regkey.setstring() requires two args <key, value>"));
		return AllocNil();
	}
	HKEY hKey = (HKEY)self->Get(_T("#hkey")).Void();
	if (hKey == NULL) {
		pInterp->RuntimeError(_T("regkey not initialized"));
		return AllocNil();
	}
	CString value = args[1].String();
	if (RegSetValueEx(hKey, args[0].String(), 0, REG_SZ, (const BYTE*)(LPCTSTR)value,
		(value.GetLength() + 1) * sizeof(TCHAR)) != ERROR_SUCCESS)
	{
		pInterp->RuntimeError(_T("regkey.setstring() failed"));
		return AllocNil();
	}
	return args[1];
}

CORE_FUNC(Regkey_Get)
{
	ASSERT_SELF(SYSTEM_MODULE_NAME);
	if ((argCount != 2) || (args[0].Type() != stString)) {
		pInterp->RuntimeError(_T("regkey.get() requires two arguments <key, default>"));
		return AllocNil();
	}
	HKEY hKey = (HKEY)self->Get(_T("#hkey")).Void();
	if (hKey == NULL) {
		pInterp->RuntimeError(_T("regkey not initialized"));
		return AllocNil();
	}

	// Need to find out how big buffer needs to be...
	// Could actually be done be seeing if the QueryValue function fails, and
	// then adjusting size and re-trying.
	DWORD bufSize = 0;
	if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &bufSize, NULL, NULL)
		!= ERROR_SUCCESS)
	{
		RegCloseKey(hKey);
		pInterp->RuntimeError(_T("regkey.get() unable to determine data size"));
		return AllocNil();
	}
	bufSize += 2;		// Does info return include NULL terminator? Probably
	BYTE* buf = new BYTE[bufSize];

	VALUE vResult;
	DWORD dwType;
	TString keyName = args[0].String();
	if (RegQueryValueEx(hKey, keyName, NULL, &dwType, (BYTE*)buf, &bufSize) != ERROR_SUCCESS) {
		vResult = args[1];	// Value not here, return specified default
	}
	else if (dwType == REG_SZ) {
		vResult = CString((LPCTSTR)buf);	// Assuming string type matches TCHAR
	}
#ifndef UNDER_CE
	else if (dwType == REG_EXPAND_SZ) {
		CString dest;
		TCHAR* destBuf = dest.GetBuffer(512);
		int length = ExpandEnvironmentStrings((LPCTSTR)buf, destBuf, 512);
		if (length > 512) {
			dest.ReleaseBuffer(512);
			destBuf = dest.GetBuffer(length);
			length = ExpandEnvironmentStrings((LPCTSTR)buf, destBuf, length);
		}
		dest.ReleaseBuffer(length);
		vResult = dest;
	}
#endif
	else if (dwType == REG_DWORD) {
		vResult = *(int*)buf;
	}
	else {
		pInterp->RuntimeError(_T("regkey.get() found unsupported value type"));
	}
	delete[] buf;
	return vResult;
}

static VALUE InitWinRegKeyClass()
{
	VALUE v = AllocClass(_T("regkey"), VALUE());
	v.Set(_T("__init__"), AllocFunction(Regkey__Init__, SYSTEM_MODULE_NAME));
	v.Set(_T("get"), AllocFunction(Regkey_Get, SYSTEM_MODULE_NAME));
	v.Set(_T("setint"), AllocFunction(Regkey_SetInt, SYSTEM_MODULE_NAME));
	v.Set(_T("setstring"), AllocFunction(Regkey_SetString, SYSTEM_MODULE_NAME));
	return v;
}

//import "system" as *
//print datetimepart('time, "5/15/2001 4:45PM"), cr
//print datetimepart('date, "5/15/2001 4:45PM"), cr
//print datetimepart('longdate, "5/15/2001 4:45PM"), cr
//print datetimepart('formattime, "5/15/2001 4:45PM", "h:mmtt"), cr
//print datetimepart('formatdate, "5/15/2001 4:45PM", "d/M/yy"), cr

CORE_FUNC(system_datetimepart)
{
	// datetimepart('time | 'date | 'longdate | 'formatdate | 'formattime, datetimestring, <formatstring>)
	if (argCount < 2) {
		ERR("Not enough args");
	}

	CString format;
	CString part = args[0].String();
	part.MakeLower();

	LPCTSTR lpFormat = NULL;
	DWORD dwFormat;
	if (part == "time") dwFormat = 0;
	else if (part == "date") dwFormat = DATE_SHORTDATE;
	else if (part == "longdate") dwFormat = DATE_LONGDATE;
	else if ((part == "formatdate") || (part == "formattime")) {
		if ((argCount != 3) && (args[2].Type() != stString)) {
			ERR("'formatdate and 'formattime require a format string in third arg");
		}
		format = args[2].String();
		dwFormat = 0;
		lpFormat = format;
	}
	else {
		ERR("First arg to datetimepart must be 'time, 'date, 'longdate, 'formatdate, or 'formattime");
	}

	VALUE vResult;
	SYSTEMTIME st;
	COleDateTime t;
	_TRY {
		if (t.ParseDateTime(args[1].String())) {
			LPTSTR buf;
			int count;
			CString d;
			t.GetAsSystemTime(st);
			if ((part == _T("time")) || (part == "formattime")) {
				buf = d.GetBuffer(128);
				count = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, lpFormat, buf, 128);
				d.ReleaseBuffer(count > 0 ? count - 1 : 0);
			}
			else {
				buf = d.GetBuffer(128);
				count = GetDateFormat(LOCALE_USER_DEFAULT, dwFormat, &st, lpFormat, buf, 128);
				d.ReleaseBuffer(count > 0 ? count - 1 : 0);
			}
			vResult = d;
		}
	}
	_CATCH_ALL {
		vResult = AllocNil();
	}
	return vResult;
}

CORE_FUNC(system_createdate_)
{
	CString d, t;
	SYSTEMTIME st;
	memset(&st, 0, sizeof(SYSTEMTIME));
	st.wYear = args[0].Integer();
	st.wMonth = args[1].Integer();
	st.wDay = args[2].Integer();
	st.wHour = args[3].Integer();
	st.wMinute = args[4].Integer();
	st.wSecond = (argCount >= 6) ? args[5].Integer() : 0;
	st.wMilliseconds = (argCount >= 7) ? args[6].Integer() : 0;

	LPTSTR buf = d.GetBuffer(128);
	int count = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buf, 128);
	d.ReleaseBuffer(count > 0 ? count - 1 : 0);
	buf = t.GetBuffer(128);
	count = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buf, 128);
	t.ReleaseBuffer(count > 0 ? count - 1 : 0);
	return d + " " + t;
}

CORE_FUNC(system_comparetime)
{
	if ((argCount != 2) || (args[0].Type() != stString) || (args[1].Type() != stString)) {
		pInterp->RuntimeError(_T("system.comparetime() requires two strings"));
		return AllocNil();
	}

	//CXTime d1, d2;
	//if (!d1.Parse(args[0].String()) || !d2.Parse(args[1].String())) {
	//	pInterp->RuntimeError(_T("system.comparetime() unable to parse date"));
	//	return AllocNil();
	//}

	int result = 10;
	COleDateTime d1, d2;
	_TRY {
		if (d1.ParseDateTime(args[0].String()) && d2.ParseDateTime(args[1].String())) {
			if (d1 == d2) result = 0;
			else if (d1 > d2) result = -1;
			else result = 1;
		}
	}
	_CATCH_ALL {

	}

	if (result == 10) {
		pInterp->RuntimeError(_T("comparetime() failed"));
	}
	return result;
}

CORE_FUNC(system_openregkey)
{
	if ((argCount < 1) || (argCount > 2)) {
		pInterp->RuntimeError(_T("system.openregkey() requires one or two arguments"));
		return AllocNil();
	}

	CString key = args[0].String();
	bool bCreate = argCount > 1 ? args[1].Boolean() : true;
	if (bCreate) {
		// Create key if it doesn't already exist
		HKEY hKey = _OpenRegKey(args[0].String(), true);
		if (hKey == NULL) {
			pInterp->RuntimeError(_T("system.openregkey() failed to create or open key: ") +
				args[0].String());
			return AllocNil();
		}
		VALUE vResult = AllocClassInstance(vRegKeyClassBase, pInterp);
		vResult.Set(_T("#hkey"), AllocVoid(hKey, freeRegKey, _T("regkey")));
		return vResult;
	}
	else {
		// If key doesn't exists return nil
		HKEY hKey = _OpenRegKey(args[0].String(), false);
		if (hKey == NULL) {
			return AllocNil();
		}
		VALUE vResult = AllocClassInstance(vRegKeyClassBase, pInterp);
		vResult.Set(_T("#hkey"), AllocVoid(hKey, freeRegKey, _T("regkey")));
		return vResult;
	}
}

#ifndef UNDER_CE
CORE_FUNC(system_getenviron)
{
	if (argCount == 0) {
		ERR("system.getenviron() requires at least on argument");
	}

	VALUE result;
	TCHAR buf[256];
	DWORD count = GetEnvironmentVariable(args[0].String(), buf, 256);
	if (count == 0) {
		if (argCount > 1) result = args[1];
		else result = AllocNil();
	}
	else if (count >= 256) {
		TCHAR* newbuf = new TCHAR[count];
		GetEnvironmentVariable(args[0].String(), newbuf, count);
		result = newbuf;
		delete[] newbuf;
	}
	else {
		result = buf;
	}
	return result;
}

CORE_FUNC(system_setenviron)
{
	if (argCount != 2) {
		ERR("system.setenviron() requires two arguments");
	}
	else if (args[1].Type() == stNil) {
		// Effectively deletes the variable from the environment
		SetEnvironmentVariable(args[0].String(), NULL);
	}
	else SetEnvironmentVariable(args[0].String(), args[1].String());
	return AllocNil();
}
#endif

#include <mmsystem.h>
CORE_FUNC(system_playsound)
{
	if ((argCount != 1) && (argCount != 2)) {
		ERR("Invalid arg count to playsound(filename, bSync)");
	}

	DWORD options = SND_SYNC;
	if (argCount >= 2) {
		options = args[1].Boolean() ? SND_SYNC : SND_ASYNC;
	}
	sndPlaySound(args[0].String(), options);
	return AllocNil();
}

#ifndef __WINCE__
CORE_FUNC(stdout_write)
{
	if (argCount != 1) {
		ERR("stdout.write() reguires one arg")
	}
	printf(args[0].String());
	return AllocNil();
}

CORE_FUNC(stdout_writeline)
{
	if ((argCount != 0) && (argCount != 1)) {
		ERR("stdout.writeline() reguires zero or one arg")
	}
	else if (argCount == 1) {
		printf(args[0].String());
	}
	printf(_T("\n"));
	return AllocNil();
}

CORE_FUNC(stdout_tab)
{
	if (argCount != 0) {
		ERR("stdout.tab() reguires zero args")
	}
	printf(_T("\t"));
	return AllocNil();
}

static VALUE InitStdoutClass()
{
	VALUE v = AllocClass(_T("stdout"), VALUE());
	v.Set(_T("write"), AllocFunction(stdout_write, SYSTEM_MODULE_NAME));
	v.Set(_T("writeline"), AllocFunction(stdout_writeline, SYSTEM_MODULE_NAME));
	v.Set(_T("tab"), AllocFunction(stdout_tab, SYSTEM_MODULE_NAME));
	return v;
}

CORE_FUNC(system_oemtoansi)
{
	if (argCount != 1) {
		ERR("oemtoansi() takes one arg");
	}
	CString temp = args[0].String();
	temp.OemToAnsi();
	return temp;
}
#endif

//////////////////////////////////////

#ifdef COMPORT_SUPPORT

#include "CommPort.h"

static void freeComPort(void* pVoid)
{
	if (pVoid) delete (CCommPort*)pVoid;
}

CORE_FUNC(Comport__Init__)
{
	if ((argCount < 1) && (argCount > 2)) {
		pInterp->RuntimeError(_T("Invalid arg count"));
	}
	else {
		DWORD baud = 9600;
		BYTE parity = NOPARITY;
		BYTE byteSize = 8;
		BYTE stopBits = ONESTOPBIT;
		if ((argCount > 1) && (args[1].Type() == stFrame)) {
			VALUE v = args[1];
			if (v[_T("baud")].Type() == stInteger) {
				baud = v[_T("baud")].Integer();
			}
			if (v[_T("bytesize")].Type() == stInteger) {
				byteSize = v[_T("bytesize")].Integer();
			}
			if ((v[_T("parity")].Type() == stString) || (v[_T("parity")].Type() == stSymbol)) {
				CString par = v[_T("parity")].String();
				par.MakeLower();
				par.TrimLeft();
				par.TrimRight();
				if ((par == _T("none")) || (par == _T("no"))) parity = NOPARITY;
				else if (par == _T("odd")) parity = ODDPARITY;
				else if (par == _T("even")) parity = EVENPARITY;
				else if (par == _T("mark")) parity = MARKPARITY;
				else if (par == _T("space")) parity = SPACEPARITY;
			}
			if ((v[_T("stopbits")].Type() == stInteger) || (v[_T("stopbits")].Type() == stFloat)) {
				float sb = v[_T("stopbits")].Float();
				if (sb == 1.0f) stopBits = 0;
				else if (sb == 1.5f) stopBits = 1;
				else if (sb == 2.0f) stopBits = 2;
			}
		}

		CCommPort* pCom = new CCommPort();
		if (!pCom->Open(args[0].String(), baud, byteSize, parity, stopBits)) {
			delete pCom;
			pInterp->RuntimeError(_T("Failed to open com port: ") + args[0].String());
		}
		else {
			self->Set(_T("#pcom"), AllocVoid(pCom, freeComPort, _T("comport")));
		}
	}
	return AllocNil();
}

CORE_FUNC(Comport_read)
{
	ASSERT_SELF(SYSTEM_MODULE_NAME);
	CCommPort* pCom = (CCommPort*)self->Get(_T("#pcom")).Void();
	if (pCom == NULL) {
		pInterp->RuntimeError(_T("comport not open"));
		return AllocNil();
	}
	return pCom->Read();
}

CORE_FUNC(Comport_write)
{
	REQUIRE_ARGS(1);
	ASSERT_SELF(SYSTEM_MODULE_NAME);
	CCommPort* pCom = (CCommPort*)self->Get(_T("#pcom")).Void();
	if (pCom == NULL) {
		pInterp->RuntimeError(_T("comport not open"));
		return AllocNil();
	}

	int bytesWritten = 0;
	if (args[0].Type() == stString) {
		bytesWritten = pCom->Write(args[0].String());
	}
	else if (args[0].Type() == stChunk) {
		VALUE vChunk = args[0];
		bytesWritten = pCom->Write(vChunk.GetChunkData(), vChunk.GetChunkByteCount());
	}
	else {
		pInterp->RuntimeError(_T("comport.write takes one string or chunk arg"));
	}
	return bytesWritten;
}

static VALUE InitComPortClass()
{
	VALUE v = AllocClass(_T("comport"), VALUE());
	v.Set(_T("__init__"), AllocFunction(Comport__Init__, SYSTEM_MODULE_NAME));
	v.Set(_T("read"), AllocFunction(Comport_read, SYSTEM_MODULE_NAME));
	v.Set(_T("write"), AllocFunction(Comport_write, SYSTEM_MODULE_NAME));
	return v;
}

#endif // COMPORT_SUPPORT

//////////////////////////////////////

#if defined(DLL_SUPPORT)
CDLLFunc::CDLLFunc(FARPROC pFunc, CallTypes calltype, CString returnType, VALUE vArgTypes)
{
	if (SetTypes(calltype, returnType, vArgTypes)) m_pFunc = pFunc;
	else m_pFunc = NULL;
}

/*bool CDLLFunc::Exec(VALUE& result, int argCount, VALUE args[])
{
	VALUE vTemp;
	int idx, temp;
	int eaxResult;
	CString strTemp;
	FARPROC p = m_pFunc;
	unsigned char * strBuffer[16];
	memset(strBuffer, 0, sizeof(unsigned char*) * 16);

	ASSERT(argCount == m_ParamCount);
	ASSERT((argCount == 0) || (args != NULL));

	// Push args on stack in reverse order
	for (idx = argCount - 1; idx >= 0; --idx) {
		switch (m_ArgTypes[idx]) {
			case atVoid:
				_asm push 0
				break;
			case atInt:
				temp = args[idx].Integer();
				_asm mov edx, temp
				_asm push edx
				break;
			case atIntp:
				if (args[idx].Type() == stNil) {
					temp = NULL;
				}
				else {
					strBuffer[idx] = new unsigned char[sizeof(int)];
					*((int*)(strBuffer[idx])) = args[idx].Integer();
					temp = (int)strBuffer[idx];
				}
				_asm mov edx, dword ptr[temp]
				_asm push edx
			case atChar:
				strTemp = args[idx].String();
				if (strTemp.GetLength() <= 0) temp = 0; //return false;
				else temp = (int)strTemp[0];
				_asm mov edx, temp
				_asm push edx
				break;
			case atCharp:
				if (args[idx].Type() == stNil) {
					temp = NULL;
				}
				else {
					strTemp = args[idx].String();
					temp = strTemp.GetLength();
					strBuffer[idx] = new unsigned char[temp + 1];
					#ifdef UNDER_CE	// UNICODE
						for (loop = 0; loop < temp; ++loop) {
							strBuffer[idx][loop] = (unsigned char)(strTemp[loop]);
						}
					#else
						memcpy(strBuffer[idx], (LPCTSTR)strTemp, temp);
					#endif
					strBuffer[idx][temp] = '\0'; // NULL terminate the string
					temp = (int)strBuffer[idx];
				}
				_asm mov edx, dword ptr[temp]
				_asm push edx
				break;
			case atStructp:
				if (args[idx].Type() == stNil) {
					temp = NULL;
				}
				else if (args[idx].Type() == stChunk) {
					temp = args[idx].GetChunkByteCount();
					strBuffer[idx] = new unsigned char[temp];
					memcpy(strBuffer[idx], args[idx].GetChunkData(), temp);
					temp = (int)strBuffer[idx];
				}
				else {
					ASSERT(false); // Need error catching (with stack pops!)
					temp = NULL;
				}
				_asm mov edx, dword ptr[temp]
				_asm push edx
				break;
			case atBool:
				temp = args[idx].Boolean() ? 1 : 0;
				_asm mov edx, temp
				_asm push edx
				break;
			default:
				ASSERT(false);
				result = AllocNil();
		}
	}

	__asm call dword ptr[p]		// Call function
	__asm mov eaxResult, eax	// Get result out of eax immediately!

	if (m_CallType == ctC) {
		// C calling convention requires us to pop stack
		for (idx = 0; idx < argCount; ++idx) {
			__asm pop edx
		}
	}

	switch (m_ReturnType) {
		case atVoid: result = AllocNil();	break;
		case atInt:  result = eaxResult;	break;
		case atChar: result = TString((TCHAR)eaxResult);	break;
		case atCharp: result = TString((LPCTSTR)eaxResult);	break;
		case atBool: result = (bool)(eaxResult != 0);		break;
		default: result = AllocNil();
	}

	// Copy the pass-by-refs back into args array
	for (idx = argCount - 1; idx >= 0; --idx) {
		switch (m_ArgTypes[idx]) {
			case atCharp:
				if (args[idx].Type() == stString) {
					strTemp = args[idx].String();
					temp = strTemp.GetLength();
					LPTSTR buf = strTemp.GetBuffer(temp);
					#ifdef UNDER_CE	// UNICODE
						for (loop = 0; loop < temp; ++loop) {
							strBuffer[idx][loop] = (unsigned char)(strTemp[loop]);
						}
					#else
						memcpy(buf, strBuffer[idx], temp);
					#endif
					strTemp.ReleaseBuffer(temp);
					args[idx] = strTemp;
				}
				break;
			case atStructp:
				if (args[idx].Type() == stChunk) {
					temp = args[idx].GetChunkByteCount();
					vTemp = AllocChunk(temp, strBuffer[idx]);
					vTemp.CopyChunkProperties(args[idx]); // Save itype, dim, etc.
					args[idx] = vTemp;
				}
				break;
			case atIntp:
				if (args[idx].Type() == stChunk) {
					args[idx] = *((int*)(strBuffer[idx]));
				}
				break;
		}
	}


	// Free the pass-by-ref memory buffers
	for (idx = 0; idx < 16; ++idx) {
		if (strBuffer[idx] != NULL) delete[] strBuffer[idx];
	}

	return true;
}*/

bool CDLLFunc::Exec(VALUE& result, int argCount, VALUE args[])
{
	VALUE vTemp;
	int idx, eaxResult, loop;
	DWORD temp;
	CString strTemp;
	FARPROC p = m_pFunc;
	unsigned char * strBuffer[10];
	DWORD dwa[10];
	memset(dwa, 0, sizeof(DWORD) * 10);
	memset(strBuffer, 0, sizeof(unsigned char*) * 10);

	ASSERT(argCount == m_ParamCount);
	ASSERT((argCount == 0) || (args != NULL));

	for (idx = 0; idx < argCount; ++idx) {
		switch (m_ArgTypes[idx]) {
			case atVoid:
				break;
			case atInt:
				dwa[idx] = args[idx].Integer();
				break;
			case atIntp:
				if (args[idx].Type() == stNil) {
					temp = NULL;
				}
				//else if (args[idx].Type() == stChunk) {
				//	temp = (DWORD)args[idx].GetChunkData();
				//}
				else {
					strBuffer[idx] = new unsigned char[sizeof(int)];
					*((int*)(strBuffer[idx])) = args[idx].Integer();
					temp = (DWORD)strBuffer[idx];
				}
				dwa[idx] = temp;
				break;
			case atChar:
			case atWChar:
				strTemp = args[idx].String();
				if (strTemp.GetLength() <= 0) temp = 0; //return false;
				else temp = (DWORD)strTemp[0];
				dwa[idx] = temp;
				break;
			case atCharp:
				if (args[idx].Type() == stNil) {
					temp = NULL;
				}
				else {
					strTemp = args[idx].String();
					temp = strTemp.GetLength();
					strBuffer[idx] = new unsigned char[temp + 1];
					#ifdef UNDER_CE	// UNICODE
						for (loop = 0; loop < temp; ++loop) {
							strBuffer[idx][loop] = (unsigned char)(strTemp[loop]);
						}
					#else
						memcpy(strBuffer[idx], (LPCTSTR)strTemp, temp);
					#endif
					strBuffer[idx][temp] = '\0'; // NULL terminate the string
					temp = (DWORD)strBuffer[idx];
				}
				dwa[idx] = temp;
				break;
			case atWCharp:
				if (args[idx].Type() == stNil) {
					dwa[idx] = NULL;
				}
				else {
					strTemp = args[idx].String();
					temp = strTemp.GetLength();
					strBuffer[idx] = (unsigned char*)(new wchar_t[temp + 1]);
					#ifdef UNDER_CE	// UNICODE
						memcpy(strBuffer[idx], (LPCTSTR)strTemp, temp * sizeof(wchar_t));
					#else
						for (loop = 0; loop < temp; ++loop) {
							((wchar_t*)strBuffer[idx])[loop] = (wchar_t)(strTemp[loop]); // ASCII to Unicode (sort of)
						}
					#endif
					((wchar_t*)strBuffer[idx])[temp] = '\0'; // NULL terminate the string
					dwa[idx] = (DWORD)strBuffer[idx];
				}
				break;
			case atStructp:
				if (args[idx].Type() == stNil) {
					dwa[idx] = NULL;
				}
				else if (args[idx].Type() == stChunk) {
					temp = args[idx].GetChunkByteCount();
					strBuffer[idx] = new unsigned char[temp];
					memcpy(strBuffer[idx], args[idx].GetChunkData(), temp);
					dwa[idx] = (DWORD)strBuffer[idx];
				}
				else {
					ASSERT(false); // Need error catching (with stack pops!)
					dwa[idx] = NULL;
				}
				break;
			case atBool:
				dwa[idx] = args[idx].Boolean() ? 1 : 0;
				break;
			default:
				ASSERT(false);
				result = AllocNil();
		}
	}

	if (m_CallType == ctC) {
		switch (argCount) {
			// It's possible all the C decl funcs could be done in one 10 arg call since the
			// C calling convention has us clean up the stack, but until it's tested, do things the long way.
			case 0: eaxResult = ((DWORD (__cdecl*)())(p))(); break;
			case 1: eaxResult = ((DWORD (__cdecl*)(DWORD))(p))(dwa[0]); break;
			case 2: eaxResult = ((DWORD (__cdecl*)(DWORD, DWORD))(p))(dwa[0], dwa[1]); break;
			case 3: eaxResult = ((DWORD (__cdecl*)(DWORD, DWORD, DWORD))(p))(dwa[0], dwa[1], dwa[2]); break;
			case 4: eaxResult = ((DWORD (__cdecl*)(DWORD, DWORD, DWORD, DWORD))(p))(dwa[0], dwa[1], dwa[2], dwa[3]); break;
			case 5: eaxResult = ((DWORD (__cdecl*)(DWORD, DWORD, DWORD, DWORD, DWORD))(p))(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4]); break;
			case 6: eaxResult = ((DWORD (__cdecl*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD))(p))
						(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4], dwa[5]); break;
			case 7: eaxResult = ((DWORD (__cdecl*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD))(p))
						(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4], dwa[5], dwa[6]); break;
			case 8: eaxResult = ((DWORD (__cdecl*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD))(p))
						(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4], dwa[5], dwa[6], dwa[7]); break;
			case 9: eaxResult = ((DWORD (__cdecl*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD))(p))
						(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4], dwa[5], dwa[6], dwa[7], dwa[8]); break;
			case 10: eaxResult = ((DWORD (__cdecl*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD))(p))
						(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4], dwa[5], dwa[6], dwa[7], dwa[8], dwa[9]); break;
		}
	}
	else {
		switch (argCount) {
			case 0: eaxResult = ((DWORD (__stdcall*)())(p))(); break;
			case 1: eaxResult = ((DWORD (__stdcall*)(DWORD))(p))(dwa[0]); break;
			case 2: eaxResult = ((DWORD (__stdcall*)(DWORD, DWORD))(p))(dwa[0], dwa[1]); break;
			case 3: eaxResult = ((DWORD (__stdcall*)(DWORD, DWORD, DWORD))(p))(dwa[0], dwa[1], dwa[2]); break;
			case 4: eaxResult = ((DWORD (__stdcall*)(DWORD, DWORD, DWORD, DWORD))(p))(dwa[0], dwa[1], dwa[2], dwa[3]); break;
			case 5: eaxResult = ((DWORD (__stdcall*)(DWORD, DWORD, DWORD, DWORD, DWORD))(p))(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4]); break;
			case 6: eaxResult = ((DWORD (__stdcall*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD))(p))
						(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4], dwa[5]); break;
			case 7: eaxResult = ((DWORD (__stdcall*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD))(p))
						(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4], dwa[5], dwa[6]); break;
			case 8: eaxResult = ((DWORD (__stdcall*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD))(p))
						(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4], dwa[5], dwa[6], dwa[7]); break;
			case 9: eaxResult = ((DWORD (__stdcall*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD))(p))
						(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4], dwa[5], dwa[6], dwa[7], dwa[8]); break;
			case 10: eaxResult = ((DWORD (__stdcall*)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD))(p))
						(dwa[0], dwa[1], dwa[2], dwa[3], dwa[4], dwa[5], dwa[6], dwa[7], dwa[8], dwa[9]); break;
		}
	}

	switch (m_ReturnType) {
		case atVoid: result = AllocNil();	break;
		case atBool: result = (bool)(eaxResult != 0);		break;
		case atInt:  result = eaxResult;	break;
		case atChar: case atWChar: result = TString((TCHAR)eaxResult);	break;
		case atCharp:
			#ifdef UNDER_CE
				/*strTemp.Empty();
				while ((char*)eaxResult) {
					strTemp += (char)(*((char*)eaxResult)++);
				}
				result = strTemp;*/
				result = TString((LPCWSTR)eaxResult);
			#else
				result = TString((LPCSTR)eaxResult);
			#endif
			break;
		case atWCharp:
			#ifdef UNDER_CE
				result = TString((LPCWSTR)eaxResult);
			#else
				/*strTemp.Empty();
				while ((wchar_t*)eaxResult) {
					strTemp += (wchar_t)(*((wchar_t*)eaxResult)++);
				}
				result = strTemp;*/
				result = TString((LPCSTR)eaxResult);
			#endif
			break;
		default: result = AllocNil();
	}

	// Copy the pass-by-refs back into args array
	for (idx = argCount - 1; idx >= 0; --idx) {
		switch (m_ArgTypes[idx]) {
			case atCharp:
				if (args[idx].Type() == stString) {
					strTemp = args[idx].String();
					temp = strTemp.GetLength();
					LPTSTR buf = strTemp.GetBuffer(temp);
					#ifdef UNDER_CE	// UNICODE
						for (loop = 0; loop < temp; ++loop) {
							buf[loop] = (wchar_t)(((unsigned char*)strBuffer[idx])[loop]);
						}
					#else
						memcpy(buf, strBuffer[idx], temp);
					#endif
					strTemp.ReleaseBuffer(temp);
					args[idx] = strTemp;

					//CString xxx = CString(strBuffer[idx]);
					//args[idx] = xxx;
				}
				break;
			case atWCharp:
				if (args[idx].Type() == stString) {
					strTemp = args[idx].String();
					temp = strTemp.GetLength();
					LPTSTR buf = strTemp.GetBuffer(temp);
					#ifdef UNDER_CE	// UNICODE
						memcpy(buf, strBuffer[idx], temp * sizeof(TCHAR));
					#else
						for (loop = 0; loop < temp; ++loop) {
							buf[loop] = (unsigned char)(((wchar_t*)strBuffer[idx])[loop]);
						}
					#endif
					strTemp.ReleaseBuffer(temp);
					args[idx] = strTemp;
				}
				break;
			case atStructp:
				if (args[idx].Type() == stChunk) {
					temp = args[idx].GetChunkByteCount();
					vTemp = AllocChunk(temp, strBuffer[idx]);
					vTemp.CopyChunkProperties(args[idx]); // Save itype, dim, etc.
					args[idx] = vTemp;
				}
				break;
			case atIntp:
				if (args[idx].Type() == stChunk) {
					args[idx] = *((int*)(strBuffer[idx]));	//????
				}
				else if (args[idx].Type() == stInteger) {
					args[idx] = *((int*)(strBuffer[idx]));
				}
				break;
		}
	}


	// Free the pass-by-ref memory buffers
	for (idx = 0; idx < 10; ++idx) {
		if (strBuffer[idx] != NULL) {
			if (m_ArgTypes[idx] == atCharp) {
				delete[] strBuffer[idx];
			}
			else {
				delete[] (wchar_t*)(strBuffer[idx]);
			}
		}
	}

	return true;
}

bool CDLLFunc::SetTypes(CallTypes callType, VALUE returnType, VALUE argTypesArray)
{
	bool bResult = false;
	m_CallType = callType;
	if (argTypesArray.Length() <= 16) {
		bResult = StringToType(returnType.String(), m_ReturnType);
		if (bResult) {
			m_ParamCount = argTypesArray.Length();
			for (int idx = 0; idx < m_ParamCount; ++idx) {
				bResult = StringToType(argTypesArray[idx + 1].String(), m_ArgTypes[idx]);
				if (!bResult) break;
			}
		}
	}
	return bResult;
}

bool CDLLFunc::StringToType(CString type, ArgTypes& result)
{
	type.MakeLower();
	if (type == "void") result = atVoid;
	//else if (type == "voidp") result = atVoidp;
	else if (type == "char") result = atChar;
	else if (type == "charp") result = atCharp;
	//else if (type == "charr") result = atCharr;
	else if (type == "wchar") result = atWChar;
	else if (type == "wcharp") result = atWCharp;
	//else if (type == "wcharr") result = atWCharr;
	else if (type == "bool") result = atBool;
	//else if (type == "boolp") result = atBoolp;
	//else if (type == "boolr") result = atBoolr;
	else if (type == "int") result = atInt;
	else if (type == "intp") result = atIntp;
	//else if (type == "intr") result = atIntr;
	//else if (type == "float") result = atFloat;
	//else if (type == "floatp") result = atFloatp;
	//else if (type == "floatr") result = atFloatr;
	//else if (type == "struct") result = atStruct;
	else if (type == "structp") result = atStructp;
	//else if (type == "structr") result = atStructr;
	else return false;

	return true;
}

static void freeDLLFunc(void* pVoid)
{
	if (pVoid) {
		delete (CDLLFunc*)pVoid;
	}
}

static void freeDLL(void* pVoid)
{
	if (pVoid) {
		FreeLibrary((HMODULE)pVoid);
	}
}

CORE_FUNC(DLL__Init__)
{
	ASSERT_SELF(SYSTEM_MODULE_NAME);
	if ((argCount != 1) || (args[0].Type() != stString)) {
		pInterp->RuntimeError(_T("system.dll() reguires one string argument"));
		return AllocNil();
	}

	CString dllName = args[0].String();
	HINSTANCE hDLL = LoadLibrary(dllName);
	if (hDLL == NULL) {
		pInterp->RuntimeError(_T("system.dll() failed to load library: ") + dllName);
	}
	else {
		self->Set(_T("#hDLL"), AllocVoid(hDLL, freeDLL, _T("hdll")));
		self->Set(_T("__dllName__"), dllName);
	}
	return AllocNil();
}

static VALUE DLL_LoadFunc(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp,
						  CDLLFunc::CallTypes callType)
{
	ASSERT_SELF(SYSTEM_MODULE_NAME);
	if (argCount < 3) {
		ERR("Invalid arg count");
	}

	HMODULE hDLL = (HMODULE)self->Get(_T("#hDLL")).Void();
	if (hDLL == NULL) {
		ERR("DLL has not been loaded.");
	}

	VALUE vArgTypes;
	CString newName = args[0].String();
	CString funcName = args[1].String();
	CString resultType = args[2].String();
	if (argCount >= 4) vArgTypes = args[3];
	else vArgTypes = AllocArray(0);

	FARPROC p = GetProcAddress(hDLL, funcName);
	if (p == NULL) {
		ERR("Failed to load DLL function: " + funcName);
	}

	CDLLFunc* pDLLFunc = new CDLLFunc(p, callType, resultType, vArgTypes);
	if (pDLLFunc->m_pFunc != NULL) {
		self->Set(newName, AllocVoid(pDLLFunc, freeDLLFunc, _T("dllfunc")));
		return true;
	}
	else {
		ERR("Error in DLL function args");
		return false;
	}
}

CORE_FUNC(DLL_LoadPFunc)
{
	return DLL_LoadFunc(self, argCount, args, pInterp, CDLLFunc::ctPascal);
}

CORE_FUNC(DLL_LoadCFunc)
{
	return DLL_LoadFunc(self, argCount, args, pInterp, CDLLFunc::ctC);
}

static VALUE InitDLLClass()
{
	VALUE v = AllocClass(_T("dll"), VALUE());
	v.Set(_T("__init__"), AllocFunction(DLL__Init__, SYSTEM_MODULE_NAME));
	v.Set(_T("__loadpfunc__"), AllocFunction(DLL_LoadPFunc, SYSTEM_MODULE_NAME));
	v.Set(_T("__loadcfunc__"), AllocFunction(DLL_LoadCFunc, SYSTEM_MODULE_NAME));
	return v;
}

#endif // defined(DLL_SUPPORT)

/////////////////////////////////////////////////////////////////

//#if !defined(UNDER_CE) //|| (UNDER_CE >= 300)
//#include <objbase.h>
//CORE_FUNC(system_createGUID)
//{
//	GUID guid;
//	if (CoCreateGuid(&guid) == S_OK) {
//		wchar_t buf[64];
//		int c = StringFromGUID2(guid, buf, 64);
//		if (c != 0) {
//			CString result;
//			for (int loop = 0; loop < c; ++loop) {
//				result += (TCHAR)(buf[loop]);
//			}
//			return result;
//		}
//	}
//	return AllocNil();
//}
//#endif

VALUE InitSystemModule()
{
	vRegKeyClassBase = InitWinRegKeyClass();

	VALUE module = AllocFrame();
	module.Set(_T("__module__"), SYSTEM_MODULE_NAME);
	//module.Set(_T("localeid"), AllocProperty(
	//	AllocFunction(system_readLocaleID, SYSTEM_MODULE_NAME),
	//	AllocFunction(system_writeLocaleID, SYSTEM_MODULE_NAME),
	//	AllocNil()));
	module.Set(_T("exec"), AllocFunction(system_exec, SYSTEM_MODULE_NAME));
	module.Set(_T("sleep"), AllocFunction(system_sleep, SYSTEM_MODULE_NAME));
	module.Set(_T("seconds"), AllocFunction(system_seconds, SYSTEM_MODULE_NAME));
	module.Set(_T("ticks"), AllocFunction(system_tickcount, SYSTEM_MODULE_NAME));
	module.Set(_T("status"), AllocFunction(system_status, SYSTEM_MODULE_NAME));
	module.Set(_T("floattodate"), AllocFunction(system_floattodate, SYSTEM_MODULE_NAME));
	module.Set(_T("datetofloat"), AllocFunction(system_datetofloat, SYSTEM_MODULE_NAME));
	module.Set(_T("parsetime"), AllocFunction(system_parsetime, SYSTEM_MODULE_NAME));
	module.Set(_T("datetimepart"), AllocFunction(system_datetimepart, SYSTEM_MODULE_NAME));
	module.Set(_T("comparetime"), AllocFunction(system_comparetime, SYSTEM_MODULE_NAME));
	module.Set(_T("createdate$"), AllocFunction(system_createdate_, SYSTEM_MODULE_NAME));
	module.Set(_T("openregkey"), AllocFunction(system_openregkey, SYSTEM_MODULE_NAME));
	module.Set(_T("regkey"), vRegKeyClassBase);
#ifdef DLL_SUPPORT
	vDLLBaseClass = InitDLLClass();
	module.Set(_T("dll"), vDLLBaseClass);
#endif
#ifdef COMPORT_SUPPORT
	vComPortClassBase = InitComPortClass();
	module.Set(_T("comport"), vComPortClassBase);
#endif
#ifndef __WINCE__
	module.Set(_T("getenviron"), AllocFunction(system_getenviron, SYSTEM_MODULE_NAME));
	module.Set(_T("setenviron"), AllocFunction(system_setenviron, SYSTEM_MODULE_NAME));
	module.Set(_T("stdout"), InitStdoutClass());
	module.Set(_T("oemtoansi"), AllocFunction(system_oemtoansi, SYSTEM_MODULE_NAME));
#endif
//#if !defined(UNDER_CE) || (UNDER_CE >= 300)
//	module.Set(_T("createguid"), AllocFunction(system_createGUID, SYSTEM_MODULE_NAME));
//#endif
	module.Set(_T("playsound"), AllocFunction(system_playsound, SYSTEM_MODULE_NAME));
	return module;
}
